#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <vector>
#include <functional>
#include "Task.hpp"
#include "channel.hpp"

class ProcessPool
{
    public:
    ProcessPool(int n, task_t w)
        : processnum(n), work(w)
    {
    }
    void InitProcessPool()
    {
        // 随后开始创建指定数量的进程
        for (int i = 0; i < processnum; ++i)
        {
            // 先有管道
            int pipefd[2];
            int n = ::pipe(pipefd);
            if (n < 0)
            {
                std::cout << "pipe error" << std::endl;
                continue;
            }
            // 再创建子进程
            pid_t pid = fork();
            if (pid < 0)
            {
                std::cout << "fork error" << std::endl;
                continue;
            }
            if (pid == 0)
            {
                for (auto &c : channels)
                {
                    ::close(c.wFd());
                }
                // 子进程
                // 关闭写端
                ::close(pipefd[1]);
                // 将子进程的标准输入重定向到管道的读端
                ::dup2(pipefd[0], 0);

                BeginWork(); // 我们可以在这里增添一个开始工作函数，代表该子进程已经进入工作逻辑，不会执行后续代码

                ::exit(0);
            }

            // 我们此次循环创建的子进程已经陷入了work逻辑中，所以执行到这里的是父进程
            // 于是我们需要关闭父进程对应的读端
            ::close(pipefd[0]);
            // 将子进程的信息添加到channel中
            channels.emplace_back(pipefd[1], pid);
        }
    }

    void DispatchTask()
    {
        int who = 0; // 从0开始，轮循着让子进程进行任务

        int num = 20; // 假如我们规定有5个任务要处理

        while (num--)
        {
            // a. 选择一个任务， 整数
            int task = tm.SelectTask();
            // b. 选择一个子进程channel
            Channel &curr = channels[who++];
            who %= channels.size();

            // 我们可以在这里输入一些打印信息
            std::cout << "######################" << std::endl;
            std::cout << "send " << task << " to " << curr.Name() << ", 任务还剩: " << num << std::endl;
            std::cout << "######################" << std::endl;

            // 我们需要把任务编号写入到这个子进程的管道中
            int n = ::write(curr.wFd(), &task, sizeof(task));
            // 当我们对应的子进程从管道中读到数据后，根据这个传过来的整数下标，就能执行相应的任务（实现在我们的BeginWrok中)
            sleep(1);
        }
    }

    void CleanProcessPool()
    {

        // for (int i=channels.size()-1 ;i>=0 ;i--)
        // {
        //     ::close(channels[i].wFd());
        //     int rid=::waitpid(channels[i].Id(), nullptr, 0);

        //     if(rid>0)
        //     {
        //         std::cout<<channels[i].Name()<<" 子进程已退出"<<std::endl;
        //     }
        // }

        for (auto &c : channels)
        {
            ::close(c.wFd());
            int rid = ::waitpid(c.Id(), nullptr, 0);

            if (rid > 0)
            {
                std::cout << c.Name() << " 子进程已退出" << std::endl;
            }
        }

        // for (auto &channel : channels)
        // {
        //     ::close(channel.wFd());
        // }

        // for(auto &c:channels)
        // {
        //     int rid=::waitpid(c.Id(), nullptr, 0);

        //     if(rid>0)
        //     {
        //         std::cout<<c.Name()<<" 子进程已退出"<<std::endl;
        //     }
        // }

    }

   
    private:
        std::vector<Channel> channels;
        int processnum;
        task_t work;
};